﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Aliyeye.Car.Net.Utility;

namespace Aliyeye.Car.Net.UI.Controls
{
    /// <summary>
    /// TODO: 
    /// dispose all brushes
    /// remove private _color variables, read color from solid brush, should be think twice as brush may not be solid (gradiant)
    /// define defaultValue tag
    /// add graphicPath to avoid unwanted color block on corners
    /// </summary>
    public partial class SmallIconButton : Control
    {
        #region private variables

        private enum EEventState
        {
            Normal,
            Hover,
            Press
        }

        public enum ERenderingStyle
        {
            Reflect,
            Flat,
            Gradient,
            ReverseGradient
        }

        public struct IconTextLayoutInfo
        {
            public int X;
            public int Y;
            public int TextMarginLeft;
            public int TextMarginTop;
        }

        const int LIGHT_INNER_BORDER = 90;

        Pen selectionPen = null;

        EEventState _pEventState = EEventState.Normal;
        EEventState _eventState = EEventState.Normal;

        Brush _normalBrush = null;
        Color _normalBackColor = Color.Empty;

        Brush _activeBrush = null;
        Color _activeBackColor = Color.Empty;

        Brush _hoverBrush = null;
        Color _hoverBackColor = Color.Empty;

        Brush _pressBrush = null;
        Color _pressBackColor = Color.Empty;

        Color _borderColor = Color.Empty;
        Pen _borderPen = null;

        #endregion

        #region constructors
        /// <summary>
        /// 
        /// </summary>
        public SmallIconButton()
        {
            this.SetStyle(ControlStyles.SupportsTransparentBackColor | 
                ControlStyles.DoubleBuffer | ControlStyles.UserPaint | 
                ControlStyles.AllPaintingInWmPaint, true);
            this.UpdateStyles();

            this.NormalBackColor = Color.Blue;
            this.Active = false;
            this.ReflectAlpha = 10;
            this.BorderRadius = 5;
            this.EventState = EEventState.Normal;
            this.Cursor = Cursors.Hand;
            this.EnableBorder = false;
            this.PressAnimation = true;
            this.BorderColor = Color.Goldenrod;
            this.SelectionColor = Color.Silver;
            this.EnableSelectionBorder = false;
            this.TextAlign = ContentAlignment.MiddleCenter;

            InitializeComponent();
        }

        #endregion

        #region properties

        public Color NormalBackColor
        {
            get
            {
                return this._normalBackColor;
            }
            set
            {
                if (this._normalBrush != null)
                {
                    this._normalBrush.Dispose();
                }
                this._normalBrush = new SolidBrush(value);
                this._normalBackColor = value;
            }
        }

        public Color NormalBackReflectedColor
        {
            get
            {
                return this.NormalBackColor.Lighten(this.ReflectAlpha);
            }
        }

        public Color ActiveBackColor
        {
            get
            {
                return this._activeBackColor;
            }
            set
            {
                this._activeBrush = new SolidBrush(value);
                this._activeBackColor = value;
            }
        }

        public Color ActiveBackReflectedColor
        {
            get
            {
                return this.ActiveBackColor.Lighten(this.ReflectAlpha);
            }
        }

        public Color HoverBackColor
        {
            get
            {
                return this._hoverBackColor;
            }
            set
            {
                this._hoverBrush = new SolidBrush(value);
                this._hoverBackColor = value;
            }
        }

        public Color HoverBackReflectedColor
        {
            get
            {
                return this.HoverBackColor.Lighten(this.ReflectAlpha);
            }
        }

        public Color PressBackColor
        {
            get
            {
                return this._pressBackColor;
            }
            set
            {
                this._pressBrush = new SolidBrush(value);
                this._pressBackColor = value;
            }
        }

        public Color PressBackReflectedColor
        {
            get
            {
                return this.PressBackColor.Lighten(this.ReflectAlpha);
            }
        }

        private EEventState EventState
        {
            get
            {
                return this._eventState;
            }
            set
            {
                //this._pEventState = this._eventState;
                this._eventState = value;
            }
        }

        #region Border Related

        public bool EnableBorder { get; set; }

        public int BorderRadius { set; get; }

        public Color BorderColor
        {
            get
            {
                return this._borderColor;
            }
            set
            {
                this._borderPen = new Pen(value);
                this._borderColor = value;
            }
        }

        #endregion

        #region Selectoin Related
        public Color SelectionColor
        {
            get
            {
                return selectionPen.Color;
            }
            set
            {
                if (selectionPen == null)
                {
                    selectionPen = new Pen(value);
                    selectionPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                }
                else
                {
                    selectionPen.Color = value;
                }
            }
        }

        public bool EnableSelectionBorder
        {
            get;
            set;
        } 
        #endregion

        #region Rendering Styles

        public ERenderingStyle NormalRenderingStyle { get; set; }
        public ERenderingStyle HoverRenderingStyle { get; set; }
        public ERenderingStyle PressRenderingStyle { get; set; }
        public ERenderingStyle ActiveRenderingStyle { get; set; }

        #endregion

        /// <summary>
        /// The alpha value of the mask which cover overing the top of button in order to make a reflect effect.
        /// </summary>
        public byte ReflectAlpha { set; get; }

        public Image Icon { set; get; }

        public ContentAlignment TextAlign { set; get; }

        /// <summary>
        /// True to active the control, control will be rendered according to active related styles.
        /// </summary>
        public bool Active { get; set; }

        

        /// <summary>
        /// True to enable animation when button pressed.
        /// will move text and icon 1 px offset to south
        /// </summary>
        public bool PressAnimation { get; set; }

        public System.Drawing.Drawing2D.SmoothingMode SmoothingMode { get; set; }

        #endregion

        protected override void OnPaint(PaintEventArgs pe)
        {
            int radius = this.BorderRadius;   
            Graphics g = pe.Graphics;

            Brush reflect = null;
            Brush normal = null;
            Pen innerBorderPen = null;

            bool drawBorder = this.EnableBorder;
            bool drawSelection = false;

            switch(EventState){
                case EEventState.Normal:
                    if (this.Active && this.ActiveBackColor != Color.Empty)
                    {
                        normal = this._activeBrush;
                        reflect = new SolidBrush(this.ActiveBackColor.Lighten(this.ReflectAlpha));

                        if (ActiveRenderingStyle == ERenderingStyle.Flat)
                        {
                            reflect.Dispose();
                            reflect = normal;
                        }
                        else if (ActiveRenderingStyle == ERenderingStyle.Gradient)
                        {
                            normal = this.CreateLinearGradientBrush(this.ActiveBackReflectedColor, this.ActiveBackColor);
                            reflect = this.CreateLinearGradientBrush(this.ActiveBackReflectedColor, this.ActiveBackColor);
                        }
                        else if (ActiveRenderingStyle == ERenderingStyle.ReverseGradient)
                        {
                            normal = this.CreateLinearGradientBrush(this.ActiveBackColor, this.ActiveBackReflectedColor);
                            reflect = this.CreateLinearGradientBrush(this.ActiveBackColor, this.ActiveBackReflectedColor);
                        }

                        drawSelection = true;
                    }
                    else
                    {
                        if (this.NormalBackColor == Color.Empty)
                        {
                            break;
                        }
                        normal = this._normalBrush;
                        reflect = new SolidBrush(this.NormalBackColor.Lighten(this.ReflectAlpha));

                        if (NormalRenderingStyle == ERenderingStyle.Flat)
                        {
                            reflect.Dispose();
                            reflect = normal;
                        }
                        else if (NormalRenderingStyle == ERenderingStyle.Gradient)
                        {
                            normal = this.CreateLinearGradientBrush(this.NormalBackReflectedColor, this.NormalBackColor);
                            reflect = this.CreateLinearGradientBrush(this.NormalBackReflectedColor, this.NormalBackColor);
                        }
                        else if (NormalRenderingStyle == ERenderingStyle.ReverseGradient)
                        {
                            normal = this.CreateLinearGradientBrush(this.NormalBackColor, this.NormalBackReflectedColor);
                            reflect = this.CreateLinearGradientBrush(this.NormalBackColor, this.NormalBackReflectedColor);
                        }
                    }
                    break;
                case EEventState.Hover:
                    normal = this._hoverBrush;
                    reflect = new SolidBrush(this.HoverBackColor.Lighten(this.ReflectAlpha));

                    if (HoverRenderingStyle == ERenderingStyle.Flat)
                    {
                        reflect.Dispose();
                        reflect = normal;
                    }
                    else if (HoverRenderingStyle == ERenderingStyle.Gradient)
                    {
                        normal = this.CreateLinearGradientBrush(this.HoverBackReflectedColor, this.HoverBackColor);
                        reflect = this.CreateLinearGradientBrush(this.HoverBackReflectedColor, this.HoverBackColor);
                    }
                    else if (HoverRenderingStyle == ERenderingStyle.ReverseGradient)
                    {
                        normal = this.CreateLinearGradientBrush(this.HoverBackColor, this.HoverBackReflectedColor);
                        reflect = this.CreateLinearGradientBrush(this.HoverBackColor, this.HoverBackReflectedColor);
                    }

                    if (this.Active)
                    {
                        drawSelection = true;
                    }

                    break;
                case EEventState.Press:
                    normal = this._pressBrush;
                    reflect = new SolidBrush(this.PressBackColor.Lighten(this.ReflectAlpha));

                    if (PressRenderingStyle == ERenderingStyle.Flat)
                    {
                        reflect.Dispose();
                        reflect = normal;
                    }
                    else if (PressRenderingStyle == ERenderingStyle.Gradient)
                    {
                        normal = this.CreateLinearGradientBrush(this.PressBackReflectedColor, this.PressBackColor);
                        reflect = this.CreateLinearGradientBrush(this.PressBackReflectedColor, this.PressBackColor);
                    }
                    else if (PressRenderingStyle == ERenderingStyle.ReverseGradient)
                    {
                        normal = this.CreateLinearGradientBrush(this.PressBackColor, this.PressBackReflectedColor);
                        reflect = this.CreateLinearGradientBrush(this.PressBackColor, this.PressBackReflectedColor);
                    }

                    drawSelection = true;
                    break;
                default:
                    break;
            }

            if (normal == null || reflect == null)
            {
                base.OnPaint(pe);
                return;
            }

            // get innerborder pen
            if (normal is SolidBrush)
            {
                innerBorderPen = new Pen(((SolidBrush)normal).Color.Lighten(LIGHT_INNER_BORDER));
            }
            else if (normal is System.Drawing.Drawing2D.LinearGradientBrush)
            {
                innerBorderPen = new Pen(((System.Drawing.Drawing2D.LinearGradientBrush)normal).LinearColors.First<Color>().Lighten(LIGHT_INNER_BORDER));
            }
            else
            {
                throw new Exception("normalBrush is incorrect type");
            }

            int yFix = this.Height % 2;

            g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
            g.SmoothingMode = this.SmoothingMode;
            float smoothingFix = 0;
            if (g.SmoothingMode == System.Drawing.Drawing2D.SmoothingMode.AntiAlias)
            {
                smoothingFix = .5F;
            }
            //g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;

            try
            {

                // fill top 
                g.FillRectangle(reflect, radius - smoothingFix, 0, this.Size.Width - radius * 2 + smoothingFix, radius);
                g.FillRectangle(reflect, 0, radius - smoothingFix, this.Size.Width, this.Size.Height / 2 - radius + smoothingFix);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            // fill bottom
            g.FillRectangle(normal, 0, this.Height / 2 - smoothingFix, this.Size.Width, this.Size.Height / 2 - radius + yFix);
            g.FillRectangle(normal, radius - smoothingFix, this.Size.Height - radius - 1, this.Size.Width - radius * 2 + smoothingFix, this.Size.Height);

            // draw corner
            // top left
            g.FillPie(reflect, new Rectangle(0, 0, radius * 2, radius * 2), -90, -90);
            // top right
            g.FillPie(reflect, new Rectangle(this.Width - radius * 2 - 1, 0, radius * 2, radius * 2), -90, 90);

            // bottom left
            g.FillPie(normal, new Rectangle(0, this.Height - radius * 2 - 1, radius * 2, radius * 2), 90, 90);
            g.FillPie(normal, new Rectangle(this.Width - radius * 2 - 1, this.Height - radius * 2 - 1, radius * 2, radius * 2), 90, -90);

            if (drawBorder)
            {
                
                this.DrawBorder(g, this._borderPen, 0, radius);

                // draw inner border 
                this.DrawBorder(g, innerBorderPen,1, radius);
            }

            if (drawSelection && this.EnableSelectionBorder)
            {
                // draw selection
                this.DrawBorder(g, selectionPen, 2, radius);
            }

            IconTextLayoutInfo layoutInfo = this.ComputeIconTextPosition(g);

            if (this.EventState == EEventState.Press && this.PressAnimation == true)
            {
                // move down text a little bit
                // simulate the animation when a button was 'pressed'
                layoutInfo.Y++;
            }

            g.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor),
                new PointF(
                    layoutInfo.X + layoutInfo.TextMarginLeft,
                    layoutInfo.Y + layoutInfo.TextMarginTop
                ));

            if (this.Icon != null)
            {
                g.DrawImage(this.Icon, layoutInfo.X, layoutInfo.Y, this.Icon.Width, this.Icon.Height);
            }

            if (reflect != normal)
            {
                reflect.Dispose();
            }
            innerBorderPen.Dispose();
            base.OnPaint(pe);
        }

        protected virtual IconTextLayoutInfo ComputeIconTextPosition(Graphics g)
        {
            IconTextLayoutInfo ret = new IconTextLayoutInfo()
            {
                X=0,
                Y=0,
                TextMarginLeft=0,
                TextMarginTop=0
            };

            SizeF total = new SizeF();
            SizeF textSize = g.MeasureString(this.Text, this.Font);

            total.Width = textSize.Width;
            total.Height = textSize.Height;
            if (this.Icon != null)
            {
                ret.TextMarginLeft = this.Icon.Width + (int)g.MeasureString("_", this.Font).Width;
                total.Width += ret.TextMarginLeft;
                if (this.Icon.Height > textSize.Height)
                {
                    total.Height = this.Icon.Height;
                    ret.TextMarginTop = (this.Icon.Height - (int)textSize.Height)/2 + 1;
                }
            }


            switch (this.TextAlign)
            {
                case ContentAlignment.MiddleCenter:
                    ret.X = this.Padding.Left + (this.Width - this.Padding.Left - this.Padding.Right - (int)total.Width) / 2;
                    ret.Y = this.Padding.Top + (this.Height - this.Padding.Top - this.Padding.Bottom - (int)total.Height) / 2;
                    break;
                case ContentAlignment.TopLeft:
                    ret.X = this.Padding.Left;
                    ret.Y = this.Padding.Top;
                    break;
                case ContentAlignment.MiddleLeft:
                    ret.X = this.Padding.Left;
                    ret.Y = this.Padding.Top + (this.Height - this.Padding.Top - this.Padding.Bottom - (int)total.Height) / 2;
                    break;
                case ContentAlignment.BottomCenter:
                    ret.X = this.Padding.Left + (this.Width - this.Padding.Left - this.Padding.Right - (int)total.Width) / 2;
                    ret.Y = this.Height - this.Padding.Bottom - (int)total.Height ;
                    break;
                default:
                    break;
            }

            return ret;
        }

        private void DrawBorder(Graphics g, Pen pen, int margin, int radius)
        {
            if (pen == null)
            {
                return;
            }

            if (radius <= margin - 2)
            {
                radius += margin - radius;
            }

            int rFix = radius % 2;

            // top
            g.DrawLine(pen, new Point(radius, margin), new Point(this.Size.Width - radius + rFix - margin, margin));

            // bottom
            g.DrawLine(pen, new Point(radius, this.Size.Height - 1 - margin), new Point(this.Size.Width - radius + rFix - margin, this.Size.Height - 1 - margin));

            // left
            g.DrawLine(pen, new Point(margin, radius), new Point(margin, this.Size.Height - radius + rFix - margin));

            // right
            g.DrawLine(pen, new Point(this.Size.Width - 1 - margin, radius), new Point(this.Size.Width - 1 - margin, this.Size.Height - radius + rFix - margin));

            // draw corner border
            // top left
            g.DrawArc(pen, new Rectangle(margin, margin, (radius - margin) * 2, (radius - margin) * 2), -90, -90);

            // top right
            g.DrawArc(pen, new Rectangle(this.Width - radius * 2 - 1 + margin, margin, (radius - margin) * 2, (radius - margin) * 2), -90, 90);

            // bottom left
            g.DrawArc(pen, new Rectangle(margin, this.Height - radius * 2 - 1 + margin, (radius - margin) * 2, (radius - margin) * 2), 90, 90);

            // bottom right
            g.DrawArc(pen, new Rectangle(this.Width - radius * 2 - 1 + margin, this.Height - radius * 2 - 1 + margin, (radius - margin) * 2, (radius - margin) * 2), 90, -90);
        }

        private Brush CreateLinearGradientBrush(Color normal, Color reflect)
        {

            return new System.Drawing.Drawing2D.LinearGradientBrush(
                new Point(0, 0), new Point(0, this.Height), normal, reflect);

        }

        #region event handler

        protected override void OnMouseEnter(EventArgs e)
        {
            if (this.HoverBackColor != Color.Empty)
            {
                this.EventState = EEventState.Hover;
                this.Invalidate();
                base.OnMouseEnter(e);
            }
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            if ((this.Active == false && this.NormalBackColor != Color.Empty) ||
                (this.Active == true && this.ActiveBackColor != Color.Empty))
            {
                this.EventState = EEventState.Normal;
                this._pEventState = this.EventState;
                this.Invalidate();
                base.OnMouseLeave(e);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (this.PressBackColor != Color.Empty)
            {
                this._pEventState = this.EventState;
                this.EventState = EEventState.Press;
                this.Invalidate();
                base.OnMouseDown(e);
            }
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            if ((this.Active == false && this.NormalBackColor != Color.Empty) ||
                (this.Active == true && this.ActiveBackColor != Color.Empty))
            {
                this.EventState = _pEventState;
                //this.EventState = EEventState.Normal;
                this.Invalidate();
                base.OnMouseUp(e);
            }
        }

        #endregion
    }
}
